package com.getan.boot.module.system.service.persistent;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.getan.boot.common.enums.EnableStatus;
import com.getan.boot.common.utils.CollectionUtils;
import com.getan.boot.common.utils.Preconditions;
import com.getan.boot.jdbc.service.GenericService;
import com.getan.boot.module.system.constant.ErrorCode;
import com.getan.boot.module.system.entity.RoleEntity;
import com.getan.boot.module.system.entity.RolePermissionEntity;
import com.getan.boot.module.system.entity.UserRoleEntity;
import com.getan.boot.module.system.repository.RoleRepository;
import com.getan.boot.module.system.service.cache.RoleCacheService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 角色管理
 *
 * @author Bruce.Gong
 * @since 1.0.0-SNAPSHOT
 */
@Slf4j
@RequiredArgsConstructor
@Service
@Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
public class RoleService extends GenericService<RoleRepository, RoleEntity> {

    private final UserRoleService userRoleService;
    private final RolePermissionService rolePermissionService;
    private final RoleCacheService roleCacheService;
    @Value(value = "${getan.cache.system.role:true}")
    private boolean useCache;

    /**
     * 校验角色名称是否唯一
     *
     * @param entity {id,pid,name}
     */
    public void checkCode(RoleEntity entity) {
        Preconditions.check(() -> exists(Wrappers.<RoleEntity>lambdaQuery()
                .eq(RoleEntity::getCode, entity.getCode())), ErrorCode.ROLE_CODE_EXISTS);
    }

    /**
     * 校验角色是否存在
     *
     * @return 角色信息
     */
    public RoleEntity checkExists(Long id) {
        RoleEntity db = getById(id);
        Preconditions.isNull(db, ErrorCode.ROLE_NOT_EXISTS);
        return db;
    }

    /**
     * 校验角色是否存在
     *
     * @param ids 配置ID
     */
    public List<RoleEntity> checkExists(List<Long> ids) {
        List<RoleEntity> dbs = listByIds(ids);
        Preconditions.check(() -> dbs.size() != ids.size(), ErrorCode.ROLE_NOT_EXISTS);
        return dbs;
    }

    /**
     * 校验角色状态
     * - 角色是否存在
     * - 角色状态是否可用
     *
     * @return 角色信息
     */
    public RoleEntity checkActive(Long id) {
        RoleEntity db = checkExists(id);
        Preconditions.check(() -> EnableStatus.DISABLE.match(db.getStatus()), ErrorCode.ROLE_DISABLED);
        return db;
    }

    /**
     * 检查角色是否停用
     *
     * @param dbs 角色
     */
    public void checkDisabled(List<RoleEntity> dbs) {
        List<String> disableCodes = dbs.stream()
                .filter(v -> EnableStatus.DISABLE.match(v.getStatus()))
                .map(RoleEntity::getCode)
                .collect(Collectors.toList());
        Preconditions.notEmpty(disableCodes, ErrorCode.ROLE_IS_DISABLED, disableCodes);
    }

    /**
     * 检查角色是否停用
     *
     * @param dbs 角色
     */
    public void checkEnable(List<RoleEntity> dbs) {
        List<String> disableCodes = dbs.stream()
                .filter(v -> EnableStatus.ENABLE.match(v.getStatus()))
                .map(RoleEntity::getCode)
                .collect(Collectors.toList());
        Preconditions.notEmpty(disableCodes, ErrorCode.ROLE_IS_ENABLE, disableCodes);
    }

    /**
     * 判断是否可修改
     *
     * @param id 角色ID
     * @return 角色信息
     */
    public RoleEntity canUpdate(Long id) {
        RoleEntity db = checkActive(id);
        checkSystem(Collections.singletonList(db), "修改");
        return db;
    }

    /**
     * 检查是否系统角色
     *
     * @param dbs     角色
     * @param operate 操作
     */
    private void checkSystem(List<RoleEntity> dbs, String operate) {
        List<String> codes = dbs.stream()
                .filter(RoleEntity::getSys)
                .map(RoleEntity::getCode)
                .collect(Collectors.toList());
        Preconditions.notEmpty(codes, ErrorCode.ROLE_IS_SYS, codes, operate);
    }

    /**
     * 检查角色下是否存在用户
     *
     * @param dbs     角色
     * @param operate 操作
     */
    private void checkHasUser(List<RoleEntity> dbs, String operate) {
        Map<Long, String> idCodeMap = dbs.stream()
                .collect(Collectors.toMap(RoleEntity::getId, RoleEntity::getCode));
        Set<Long> roleIds = userRoleService.listDistinctObjs(Wrappers.<UserRoleEntity>lambdaQuery()
                .select(UserRoleEntity::getRoleId)
                .in(UserRoleEntity::getRoleId, idCodeMap.keySet()), UserRoleEntity::getRoleId);
        if (CollectionUtils.isNotEmpty(roleIds)) {
            List<String> codes = idCodeMap.entrySet().stream()
                    .filter(v -> roleIds.contains(v.getKey()))
                    .map(Map.Entry::getValue)
                    .collect(Collectors.toList());
            Preconditions.notEmpty(codes, ErrorCode.ROLE_HAS_USER, codes, operate);
        }
    }

    /**
     * 检查角色是否可删除
     *
     * @param ids 角色ID
     */
    public void canDelete(List<Long> ids) {
        List<RoleEntity> dbs = checkExists(ids);
        checkSystem(dbs, "删除");
        checkHasUser(dbs, "删除");
    }

    /**
     * 判断是否可停用
     *
     * @param ids 角色ID
     * @return 角色信息
     */
    public List<RoleEntity> canDisable(List<Long> ids) {
        List<RoleEntity> dbs = checkExists(ids);
        checkDisabled(dbs);
        checkSystem(dbs, "停用");
        checkHasUser(dbs, "停用");
        return dbs;
    }

    /**
     * 判断是否可启用
     *
     * @param ids 角色ID
     * @return 角色信息
     */
    public List<RoleEntity> canEnable(List<Long> ids) {
        List<RoleEntity> dbs = checkExists(ids);
        checkEnable(dbs);
        return dbs;
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void deleteRole(List<Long> ids) {
        deleteBatch(ids);
        // 删除用户角色关系
        userRoleService.delete(Wrappers.<UserRoleEntity>lambdaUpdate()
                .in(UserRoleEntity::getRoleId, ids));
        // 删除角色权限关系
        rolePermissionService.delete(Wrappers.<RolePermissionEntity>lambdaUpdate()
                .in(RolePermissionEntity::getRoleId, ids));
    }


    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void disable(List<Long> ids) {
        update(Wrappers.<RoleEntity>lambdaUpdate()
                .set(RoleEntity::getStatus, EnableStatus.DISABLE.getCode())
                .in(RoleEntity::getId, ids)
                .eq(RoleEntity::getStatus, EnableStatus.ENABLE.getCode()));
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void enable(List<Long> ids) {
        update(Wrappers.<RoleEntity>lambdaUpdate()
                .set(RoleEntity::getStatus, EnableStatus.ENABLE.getCode())
                .in(RoleEntity::getId, ids)
                .eq(RoleEntity::getStatus, EnableStatus.DISABLE.getCode()));
    }

    private void addUser(Long roleId, List<Long> userIds) {
        userRoleService.insertBatch(userIds.stream()
                .map(v -> UserRoleEntity.builder()
                        .roleId(roleId)
                        .userId(v)
                        .build())
                .collect(Collectors.toList()));
    }

    public Set<Long> permissionIds(Long roleId) {
        return rolePermissionService.listDistinctObjs(Wrappers.<RolePermissionEntity>query()
                .select("distinct permission_id")
                .lambda().setEntityClass(RolePermissionEntity.class)
                .eq(RolePermissionEntity::getRoleId, roleId), RolePermissionEntity::getPermissionId);
    }

    private void addPermission(Long roleId, List<Long> permissionIds) {
        rolePermissionService.insertBatch(permissionIds.stream()
                .map(v -> RolePermissionEntity.builder()
                        .roleId(roleId)
                        .permissionId(v)
                        .build())
                .collect(Collectors.toList()));
    }

    /**
     * 为角色分配用户
     *
     * @param roleId  角色ID
     * @param userIds 用户列表
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void allocUser(Long roleId, List<Long> userIds) {
        if (CollectionUtils.isEmpty(userIds)) {
            userRoleService.delete(Wrappers.<UserRoleEntity>lambdaUpdate()
                    .eq(UserRoleEntity::getRoleId, roleId));
        } else {
            Set<Long> dbUserIds = userRoleService.listDistinctObjs(Wrappers.<UserRoleEntity>lambdaQuery()
                    .select(UserRoleEntity::getUserId)
                    .eq(UserRoleEntity::getRoleId, roleId), UserRoleEntity::getUserId);
            if (CollectionUtils.isEmpty(dbUserIds)) {
                addUser(roleId, userIds);
            } else {
                userIds.removeIf(dbUserIds::remove);
                if (CollectionUtils.isNotEmpty(userIds)) {
                    addUser(roleId, userIds);
                }
                if (CollectionUtils.isNotEmpty(dbUserIds)) {
                    userRoleService.delete(Wrappers.<UserRoleEntity>lambdaUpdate()
                            .eq(UserRoleEntity::getRoleId, roleId)
                            .in(UserRoleEntity::getUserId, dbUserIds));
                }
            }
        }
    }

    /**
     * 为角色分配权限
     *
     * @param roleId        角色ID
     * @param permissionIds 权限列表
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void allocPermission(Long roleId, List<Long> permissionIds) {
        if (CollectionUtils.isEmpty(permissionIds)) {
            rolePermissionService.delete(Wrappers.<RolePermissionEntity>lambdaUpdate()
                    .eq(RolePermissionEntity::getRoleId, roleId));
        } else {
            Set<Long> dbPermissionIds = rolePermissionService.listDistinctObjs(Wrappers.<RolePermissionEntity>lambdaQuery()
                    .select(RolePermissionEntity::getPermissionId)
                    .eq(RolePermissionEntity::getRoleId, roleId), RolePermissionEntity::getPermissionId);
            if (CollectionUtils.isEmpty(dbPermissionIds)) {
                addPermission(roleId, permissionIds);
            } else {
                permissionIds.removeIf(dbPermissionIds::remove);
                if (CollectionUtils.isNotEmpty(permissionIds)) {
                    addPermission(roleId, permissionIds);
                }
                if (CollectionUtils.isNotEmpty(dbPermissionIds)) {
                    rolePermissionService.delete(Wrappers.<RolePermissionEntity>lambdaUpdate()
                            .eq(RolePermissionEntity::getRoleId, roleId)
                            .in(RolePermissionEntity::getPermissionId, dbPermissionIds));
                }
            }
        }
    }

    // region 鉴权

    /**
     * 查询用户的角色ID集合，并在 session 中进行缓存
     *
     * @param userId 用户ID
     * @return 角色信息
     */
    public Set<Long> queryUserRoleIds(Long userId) {
        return userRoleService.listDistinctObjs(Wrappers.<UserRoleEntity>query()
                .select("distinct role_id")
                .lambda().setEntityClass(UserRoleEntity.class)
                .eq(UserRoleEntity::getUserId, userId), UserRoleEntity::getRoleId);
    }

    /**
     * 根据角色ID查找角色信息
     */
    public List<RoleEntity> listActiveRoles(Set<Long> roleIds) {
        if (useCache) {
            List<RoleEntity> roles = roleCacheService.cacheActive();
            return roles.stream().filter(v -> roleIds.contains(v.getId())).collect(Collectors.toList());
        } else {
            return list(Wrappers.<RoleEntity>lambdaQuery()
                    .select(RoleEntity::getId, RoleEntity::getCode)
                    .in(RoleEntity::getId, roleIds)
                    .eq(RoleEntity::getStatus, EnableStatus.ENABLE.getCode()));
        }
    }

    /**
     * 根据角色ID查找角色信息 selector
     */
    public List<RoleEntity> listActiveRoles() {
        if (useCache) {
            return roleCacheService.cacheActive();
        } else {
            return list(Wrappers.<RoleEntity>lambdaQuery()
                    .select(RoleEntity::getId, RoleEntity::getCode, RoleEntity::getName)
                    .eq(RoleEntity::getStatus, EnableStatus.ENABLE.getCode()));
        }
    }

    // endregion

    public void removeCache() {
        try {
            if (useCache) {
                roleCacheService.removeCache();
            }
        } catch (Exception e) {
            log.error("删除缓存失败[role]", e);
        }
    }

}
